//https://dart.dev/tools/diagnostic-messages?utm_source=dartdev&utm_medium=redir&utm_id=diagcode&utm_content=uri_does_not_exist#uri_does_not_exist
// import 'package:characters/characters.dart';

import 'dart:math';

/*
  https://dart.cn/guides/language/language-tour

  重要概念

  所有变量引用的都是 对象，每个对象都是一个 类 的实例。数字、函数以及 null 都是对象。除去 null 以外（如果你开启了 空安全）, 所有的类都继承于 Object 类。

 */
void main() {
  //使用三个单引号或者三个双引号也能创建多行字符串：
  var s1 = '''
  You can create
  multi-line strings like this one.
  ''';

  var s2 = """
  This is also a
  multi-line string.
  """;

  print("$s1");
  print("------------------------------------------------------------------");
  print("$s2");
  print("------------------------------------------------------------------");

  //在字符串前加上 r 作为前缀创建 “raw” 字符串（即不会被做任何处理（比如转义）的字符串）：
  var s_r = r'In a raw string, not even \n gets special treatment.';
  //var s = r'在 raw 字符串中，转义字符串 \n 会直接输出 “\n” 而不是转义为换行。';
  print(s_r);
  print("------------------------------------------------------------------");
  var s_no_r = 'In a raw string, not even \n gets special treatment.';
  print(s_no_r); //输出的会换行
  print("------------------------------------------------------------------");

  //布尔类型
  // Dart 使用 bool 关键字表示布尔类型，布尔类型只有两个对象 true 和 false，两者都是编译时常量。
  bool flag = true;

  //Lists
  // 数组 (Array) 是几乎所有编程语言中最常见的集合类型，在 Dart 中数组由 List 对象表示。通常称之为 List。
  //
  // Dart 中的列表字面量是由逗号分隔的一串表达式或值并以方括号 ([]) 包裹而组成的。下面是一个 Dart List 的示例：
  var listNumb = [1, 2, 3];
  //这里 Dart 推断出 list 的类型为 List<int>，如果往该数组中添加一个非 int 类型的对象则会报错

  //你可以在 Dart 的集合类型的最后一个项目后添加逗号。这个尾随逗号并不会影响集合，但它能有效避免「复制粘贴」的错误。
  var list2 = [
    'Car',
    'Boat',
    'Plane',
  ];

  //List 的下标索引从 0 开始，第一个元素的下标为 0，最后一个元素的下标为 list.length - 1。你可以像 JavaScript 中的用法那样获取 Dart 中 List 的长度以及元素：

  if (listNumb.length > 2) {
    print(listNumb[2]);
  }
  print("------------------------------------------------------------------");

  //在 List 字面量前添加 const 关键字会创建一个编译时常量：
  var constList = const [1, 2, 3];
  print("constList[1] = ${constList[1]}"); //constList[1] = 2
  print("------------------------------------------------------------------");

  //Dart 在 2.3 引入了 扩展操作符（...）和 空感知扩展操作符（...?），它们提供了一种将多个元素插入集合的简洁方法。
  //
  // 例如，你可以使用扩展操作符（...）将一个 List 中的所有元素插入到另一个 List 中：

  var listNumb2 = [0, ...listNumb];
  if (listNumb2.length > 3) {
    print('listNumb2[0]  = ${listNumb2[0]} , listNumb2[3] = ${listNumb2[3]} ');
    //listNumb2[0]  = 0 , listNumb2[3] = 3
  }

  //如果扩展操作符右边可能为 null ，你可以使用 null-aware 扩展操作符（...?）来避免产生异常：
  var listNumb3 = [0, ...?listNumb2];
  if (listNumb3.length > 4) {
    print(
        'listNumb3[0]  = ${listNumb3[0]} , listNumb3[1]  = ${listNumb3[1]} , listNumb3[4] = ${listNumb3[4]} ');
    //listNumb3[0]  = 0 , listNumb3[1]  = 0 , listNumb3[4] = 3
  }
  print("------------------------------------------------------------------");

  //Dart 还同时引入了 集合中的 if 和 集合中的 for 操作，在构建集合时，可以使用条件判断 (if) 和循环 (for)。
  //
  // 下面示例是使用 集合中的 if 来创建一个 List 的示例，它可能包含 3 个或 4 个元素：

  bool promoActive = true;
  var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];

  //下面是使用 集合中的 for 将列表中的元素修改后添加到另一个列表中的示例：
  var listOfStrings = ['#0', for (var i in listNumb) '#$i'];
  if (listOfStrings[1] == '#1') {
    print("#1 匹配成功");
  }

  print("------------------------------------------------------------------");

  //Sets
  // 在 Dart 中，set 是一组特定元素的无序集合。 Dart 支持的集合由集合的字面量和 Set 类提供。
  //尽管 Set 类型(type) 一直都是 Dart 的一项核心功能，但是 Set 字面量(literals) 是在 Dart 2.2 中才加入的。
  var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
  //Dart 推断 halogens 变量是一个 Set<String> 类型的集合，如果往该 Set 中添加类型不正确的对象则会报错。

  //可以使用在 {} 前加上类型参数的方式创建一个空的 Set，或者将 {} 赋值给一个 Set 类型的变量：
  var names = <String>{};
  Set<String> names2 = {};
  var names3 = {};

  //Set 还是 map? Map 字面量语法相似于 Set 字面量语法。因为先有的 Map 字面量语法，所以 {} 默认是 Map 类型。
  // 如果忘记在 {} 上注释类型或赋值到一个未声明类型的变量上，那么 Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。

  //使用 add() 方法或 addAll() 方法向已存在的 Set 中添加项目：
  var elements = <String>{};
  elements.add('fluorine');
  elements.addAll(halogens);

  print("------------------------------------------------------------------");

  //从 Dart 2.3 开始，Set 可以像 List 一样支持使用扩展操作符（... 和 ...?）以及 Collection if 和 for 操作。

  //Maps
  // 通常来说，Map 是用来关联 keys 和 values 的对象。其中键和值都可以是任何类型的对象。
  // 每个 键 只能出现一次但是 值 可以重复出现多次。 Dart 中 Map 提供了 Map 字面量以及 Map 类型两种形式的 Map。
  var gifts = {
    'first': 'partridge',
    'second': 'turtledoves',
    'fifth': 'golden rings'
  };

  var nobleGases = {
    2: 'helium',
    10: 'neon',
    18: 'argon',
  };

  //Dart 将 gifts 变量的类型推断为 Map<String, String>，而将 nobleGases 的类型推断为 Map<int, String>。如果你向这两个 Map 对象中添加不正确的类型值，将导致运行时异常

  //可以使用 Map 的构造器创建 Map：
  var gifts2 = Map<String, String>();
  gifts2['first'] = 'partridge 2';
  gifts2['second'] = 'turtledoves 2';
  gifts2['fifth'] = 'golden rings 2';

  //如果检索的 Key 不存在于 Map 中则会返回一个 null：
  if (gifts2['first2'] == null) {
    print('first2 key 不存在');
  }

  print("------------------------------------------------------------------");

  print("${isNoble(10)}");
  print("${isNoble2(11)}");
  print("${isNoble3(11)}");
  //false
  // true
  // true

  print("------------------------------------------------------------------");

  enableFlags(bold: true, hidden: false);
  enableFlags2(bold: true);
  
  String sayInfo = say(('xiao'), '随机讲讲');
  print(sayInfo);
  print("----------------------------  函数是一级对象  --------------------------------------");

  listNumb.forEach(printElement);

  print("------------------------------------------------------------------");

  const list_fruit = ['apples', 'bananas', 'oranges'];
  // list_fruit.map((item) {
  //   return item.toUpperCase();
  // }).forEach((item) {
  //   print('$item: ${item.length}');
  // });

  //如果函数体内只有一行返回语句，你可以使用胖箭头缩写法。粘贴下面代码到 DartPad 中并点击运行按钮，验证两个函数是否一致。
  list_fruit.map((item) => item.toUpperCase()).forEach((item) {
    print('$item: ${item.length}');
  });

  print("------------------------------------------------------------------");

}

//函数
// Dart 是一种真正面向对象的语言，所以即便函数也是对象并且类型为 Function，这意味着函数可以被赋值给变量或者作为其它函数的参数。你也可以像调用函数一样调用 Dart 类的实例

//虽然高效 Dart 指南建议在 公开的 API 上定义返回类型，不过即便不定义，该函数也依然有效：
bool isNoble(int atomicNumber) {
  return atomicNumber == 11;
}

isNoble2(int atomicNumber) {
  return atomicNumber == 11;
}

//如果函数体内只包含一个表达式，你可以使用简写语法：
bool isNoble3(int atomicNumber) => atomicNumber == 11;
//语法 => 表达式 是 { return 表达式; } 的简写， => 有时也称之为 箭头 函数。
//在 => 与 ; 之间的只能是 表达式 而非 语句。比如你不能将一个 if语句 放在其中，但是可以放置 条件表达式。


//命名参数
// 命名参数默认为可选参数，除非他们被特别标记为 required。
//
// 定义函数时，使用 {参数1, 参数2, …} 来指定命名参数：如果你没有提供一个默认值，也没有使用 required 标记的话，那么它一定可空的类型，因为他们的默认值会是 null：

// 当调用函数时，你可以使用 参数名: 参数值 指定一个命名参数的值。
void enableFlags({bool? bold, bool? hidden}){}

//可以使用 = 来为一个命名参数指定除了 null 以外的默认值。指定的默认值必须要为编译时的常量，例如：
void enableFlags2({bool bold = true, bool hidden = false}){}

//如果你希望一个命名参数是强制需要使用的，调用者需要提供它的值，则你可以使用 required 进行声明：
// const Scrollbar({super.key, required Widget child});
//一个标记了 required 的参数仍然是可空的类型:



/*

  可选的位置参数
  使用 [] 将一系列参数包裹起来，即可将其标记为位置参数，因为它们的默认值是 null，所以如果你没有提供默认值的话，它们的类型必须得是允许为空 (nullable) 的类型。
 */

String say( String from, String msg, [String? device]){
  var result = '$from says $msg';
  if(device != null){
    result = '$result with a $device';
  }

  return result;
}

/*
  函数是一级对象
  可以将函数作为参数传递给另一个函数。
 */

void printElement(int element){
  print(element);
}

/*
  所有的函数都有返回值。没有显式返回语句的函数最后一行默认为执行 return null;。

 */